package esi.ws;

import java.util.HashMap;
import java.util.Map;

import javax.jws.WebService;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.ws.soap.SOAPFaultException;

import org.apache.commons.lang.StringUtils;

import com.netflix.api.client.APIEndpoints;
import com.netflix.api.client.NetflixAPIClient;
import com.thoughtworks.xstream.XStream;

@WebService(endpointInterface = "esi.ws.MovieService")
public class MovieServiceImpl implements MovieService {

    @Override
    public SearchMoviesResponse searchMovies(String searchTerm) throws SOAPException {
        if (StringUtils.isBlank(searchTerm)) {
            throw new SOAPFaultException(SOAPFactory.newInstance().createFault("Search term must not be empty!", new QName("esi.ws", "emptySearchTerm")));
        }

        NetflixAPIClient client = new NetflixAPIClient(MovieService.CONSUMER_KEY, MovieService.SECRET);
        String uri = APIEndpoints.CATALOG_URI;
        Map<String, String> params = new HashMap<String, String>();
        params.put("term", searchTerm);
        String responseXml = null;

        try {
            responseXml = client.makeConsumerSignedApiCall(uri, params, NetflixAPIClient.GET_METHOD_TYPE).getResponseBody();
        } catch (Exception e) {
            throw new SOAPException(e);
        }

        XStream xstream = new XStream();
        xstream.processAnnotations(new Class[] { SearchMoviesResponse.class, MovieResponse.class });
        SearchMoviesResponse response = (SearchMoviesResponse) xstream.fromXML(responseXml);
        return response;
    }

    @Override
    public MovieResponse getMovie(String movieId) throws SOAPException {
        if (StringUtils.isBlank(movieId)) {
            throw new SOAPFaultException(SOAPFactory.newInstance().createFault("Movie id must be specified!", new QName("esi.ws", "missingMovieId")));
        }

        NetflixAPIClient client = new NetflixAPIClient(MovieService.CONSUMER_KEY, MovieService.SECRET);
        String uri = APIEndpoints.MOVIE_URI + "/" + movieId;

        String reponseXml = null;
        try {
            reponseXml = client.makeConsumerSignedApiCall(uri, null, NetflixAPIClient.GET_METHOD_TYPE).getResponseBody();
        } catch (Exception e) {
            throw new SOAPException(e);
        }

        XStream xstream = new XStream();
        xstream.processAnnotations(new Class[] { MovieResponse.class });
        MovieResponse response = (MovieResponse) xstream.fromXML(reponseXml);
        return response;
    }

    @Override
    public SearchMoviesResponse browseMovies(String searchTerm, int resultsPerPage, int pageNr) throws SOAPException {
        if (StringUtils.isBlank(searchTerm)) {
            throw new SOAPFaultException(SOAPFactory.newInstance().createFault("Search term must not be empty!", new QName("esi.ws", "emptySearchTerm")));
        }

        if (resultsPerPage < 0 || resultsPerPage > 100) {
            throw new SOAPFaultException(SOAPFactory.newInstance().createFault("Results per page must be positive and smaller than 100!",
                    new QName("esi.ws", "invalidResultsPerPage")));
        }

        if (pageNr < 0) {
            throw new SOAPFaultException(SOAPFactory.newInstance().createFault("Page number must be positive!", new QName("esi.ws", "negativePageNr")));
        }

        NetflixAPIClient client = new NetflixAPIClient(MovieService.CONSUMER_KEY, MovieService.SECRET);
        String uri = APIEndpoints.CATALOG_URI;
        Map<String, String> params = new HashMap<String, String>();
        params.put("term", searchTerm);
        params.put("max_results", Integer.toString(resultsPerPage));
        params.put("start_index", Integer.toString(pageNr * resultsPerPage));
        String responseXml = null;

        try {
            responseXml = client.makeConsumerSignedApiCall(uri, params, NetflixAPIClient.GET_METHOD_TYPE).getResponseBody();
        } catch (Exception e) {
            throw new SOAPException(e);
        }

        XStream xstream = new XStream();
        xstream.processAnnotations(new Class[] { SearchMoviesResponse.class, MovieResponse.class });
        SearchMoviesResponse response = (SearchMoviesResponse) xstream.fromXML(responseXml);
        return response;
    }
}